/*
   FtpClient.java
*/

import java.net.*;
import java.io.*;
import java.util.*;

public class FtpClient
{
  private Socket m_socket;
  private ConnectionListener m_listener;
  private FtpCommon.Mode mode = FtpCommon.Mode.Binary;
  private LinkedList<String> lastCommand = null;
  private FtpCmdFiles m_clientList = new FtpCmdFiles(".");
  private static boolean m_serverReady = false;

  //===================================================

  public static void main(String args[])
  {
    FtpClient fc = null;

    if ( args.length < 1 ) 
	fc = new FtpClient(null);
    else
    	fc = new FtpClient(args[0]);

    while(true)
    {
      if(m_serverReady == true)
      {
        fc.getUserCommand();  //waits here for user commands
        m_serverReady = false;
      }

      try{Thread.sleep(150);}
      catch(InterruptedException e) {System.out.printf("Exception: %s\n", e);}
      //System.out.println("I'm looping");
      /* The sleeping helps with the race condition.  It allows 
         just enough time for a hand shake back and forth between
         the client and server during which time m_serverReady is
         set back to true before this while loop loops again.
         The sleep time is small enough so you don't notice a pause.*/

      /* Uncomment the println and comment the sleeping to see
         the difference. */

    }//ends while()
  }//ends main()

  //===================================================

  public FtpClient(String addr)
  {
    if(addr==null)addr=ScreenIO.promptForString("Enter host/IP address: ");
    try
    {
      m_socket = new Socket(addr,8000);
    }
    catch(UnknownHostException e){}
    catch(IOException e){}
    m_listener = new ConnectionListener(m_socket);
    m_listener.start();
  }

  //===================================================

  private void getUserCommand()
  {
    String prompt = ScreenIO.promptForString("ftp>", ScreenIO.left);
    LinkedList<String>  cmd = FtpCommon.tokenizeString(prompt);
    char choice = '~';

    if(cmd.size() == 0)
    {
      /*If you get to this point, that means the user did not type
        in anything.  So we send a 'ClientReady' to the server
        in order to get a 'ServerReady' response back so we can
        display the 'ftp>' prompt again.*/
      FtpCommon.sendCommand("ClientReady", m_socket);
      return;
    }

    if(FtpCommon.cmdMap.get(cmd.get(0)) != null)
      choice =((String)(FtpCommon.cmdMap.get(cmd.get(0)))).charAt(0); 

    switch (choice)
    {
      case '0':         //!
        System.exit(0);
      
      case '1':         //?
        if(cmd.size() > 1)
        {
          for(int i = 1; i < cmd.size(); i++)
          {
            FtpCommon.help(new PrintWriter(System.out), cmd.get(i));
          }
        }
        else  FtpCommon.help(new PrintWriter(System.out));
        break;
      
      case '6':         //bye
        System.exit(0);
      
      case '8':         //close
        System.exit(0);

      case 'a':         //delete file
        if(cmd.size() > 1)
          if(m_clientList.rmFile(cmd.get(1).toString()))
            System.out.printf("File '%s' removed successfully.\n", cmd.get(1).toString());
          else
            System.out.printf("Unable to remove file '%s'.\n", cmd.get(1).toString());
        else
          System.out.println("Usage: delete [ filename ]");
        break;
        
      case 'c':         //disconnect
        System.exit(0);

      case 'g':         //help
        if(cmd.size() > 1)
        {
          for(int i = 1; i < cmd.size(); i++)
          {
            FtpCommon.help(new PrintWriter(System.out), cmd.get(i));
          }
        }
        else  FtpCommon.help(new PrintWriter(System.out));
        break;

      case 'h':         //lcd or lcwd
      case 'i':
        if(cmd.size() > 1)
          m_clientList.cdAbsolute(m_clientList.getDir() + cmd.get(1));
        break;

      case 'k':         //lls
        System.out.println("===============");
        System.out.println("  Client Side  ");
        System.out.println("===============");
        String temp = m_clientList.getDir();
        if(cmd.size() > 1)
        {
          for (int i = 1; i < cmd.size(); i++)
          {
            m_clientList.setDir((String)(cmd.get(i)));
            m_clientList.listDirectory();
          }
        }
        else
        { 
          m_clientList.setDir(temp);
          m_clientList.listDirectory();
        }
        break;

      case 'l':         //lmkdir
        //FtpCmdFiles.lmkDir();
        break;

      case 'm':         //lpwd
        System.out.println("===============");
        System.out.println("  Client Side  ");
        System.out.println("===============");
        System.out.println(m_clientList.lpwd());
        break;

      case 'r':         //mkdir
        if(cmd.size() > 1)
          if(FtpCmdFiles.mkDir(cmd.get(1).toString()))
            System.out.printf("Directory '%s' made successfully.\n", cmd.get(1).toString());
          else
            System.out.printf("Unable to make directory '%s'.\n", cmd.get(1).toString());
        else
          System.out.println("Usage: mkdir [ dir ]");
        break;

      case '$':         //rmdir
        if(cmd.size() > 1)
          if(FtpCmdFiles.rmDir(cmd.get(1).toString()))
            System.out.printf("Directory '%s' removed successfully.\n", cmd.get(1).toString());
          else
            System.out.printf("Unable to remove directory '%s'.\n", cmd.get(1).toString());
        else
          System.out.println("Usage: rmdir [ dir ]");
        break;

      case 'v':         //prompt
        if (cmd.size() > 1)
        {
          FtpCommon.setPrompt(((String)(cmd.get(1))), new PrintWriter(System.out));
          System.out.printf("Prompt is %s.\n", FtpCommon.getPrompt() ? "on" : "off");
        }
        else
          System.out.println("Usage: prompt [ on | off ]");
        break;

      default:
        processUserCommand(cmd);
        break;
    }
    FtpCommon.sendCommand("ClientReady", m_socket);
  }

  //===================================================

  private class ConnectionListener extends Thread
  {
    private Socket m_socket;
    public ConnectionListener(Socket inSocket)

    {
      m_socket = inSocket;
    }
    public void run()
    {
      try
      {
        BufferedReader in = new BufferedReader
          (new InputStreamReader(m_socket.getInputStream()));
        String s = null;
        s = in.readLine();

        while (s != null)
        {
          if(s.startsWith("MESSAGE:"))
          {
            s = s.substring(8);
            System.out.println(s);
          }
          else if(s.startsWith("COMMAND:"))
          {
            LinkedList<String> cmds;
            s = s.substring(8);
            s = s.trim();
            cmds = FtpCommon.tokenizeString(s);
            processServerCommand(cmds);
          }
          else
            System.out.printf("(*ERROR*): unrecognized response: %s", s);

          s = in.readLine();
        }
      }
      catch(IOException e)
      {
        e.printStackTrace();
      }
    }
  }

  //===================================================

  boolean processServerCommand(LinkedList<String> cmds)
  {
    if(cmds.get(0).equals("ServerReady"))
    {
      m_serverReady = true;
      //System.out.print("ftp> ");  
      return true;
    }
    else if(cmds.get(0).equals("bin"))
    {
      mode = FtpCommon.Mode.Binary;
      System.out.printf("Setting mode to binary\n");
      FtpCommon.sendCommand("ClientReady", m_socket);
      return true;
    }
    else if(cmds.get(0).equals("ascii"))
    {
      mode = FtpCommon.Mode.Ascii;
      System.out.printf("Setting mode to ascii\n");
      FtpCommon.sendCommand("ClientReady", m_socket);
      return true;
    }
    else if(cmds.get(0).toString().startsWith("ls"))
    {
      String dir = cmds.get(0).toString();
      dir = dir.substring(2);
      String tempClientDir = m_clientList.getDir();/*temp storage of client path*/
      
      System.out.println("===============");
      System.out.println("  Server Side  ");
      System.out.println("===============");

      if(cmds.size() > 1)
      {
        for (int i = 1; i < cmds.size(); i++)
        {
          m_clientList.setDir((String)(cmds.get(i)));
          m_clientList.listDirectory();
        }
      }
      else 
      {
        m_clientList.setDir(dir);
        m_clientList.listDirectory();
      }
      m_clientList.setDir(tempClientDir);/*change client path back*/
    }
    else if(cmds.get(0).toString().startsWith("pwd"))
    {
      String dir = cmds.get(0).toString();
      dir = dir.substring(3);
      String tempClientDir = m_clientList.getDir();
      m_clientList.setDir(dir);
      
      System.out.println("===============");
      System.out.println("  Server Side  ");
      System.out.println("===============");
      System.out.println(m_clientList.lpwd());

      m_clientList.setDir(tempClientDir);
    }
    return false;
  }

  //===================================================

  public void processUserCommand(LinkedList<String> cmds)
  {
    if(cmds.get(0).startsWith("quit") || cmds.get(0).equals("q"))
      System.exit(0);

    if(cmds.get(0).startsWith("bin"))
    {
      lastCommand = cmds;
      FtpCommon.sendCommand("bin", m_socket);
    }
    else if(cmds.get(0).startsWith("ascii"))
    {
      lastCommand = cmds;
      FtpCommon.sendCommand("ascii", m_socket);
    }
    else
    {
      if(FtpCommon.isValidCommand(cmds.get(0).toString()))
      {
        if(cmds.size() > 1)
        {
          String temp = "";
          for(int i = 0; i < cmds.size(); i++)
            temp += cmds.get(i) + " ";
          FtpCommon.sendCommand(temp, m_socket);
        }
        else
          FtpCommon.sendCommand(cmds.get(0).toString(), m_socket);
      }
      else
        System.out.printf("%s is not a Valid Command\n", cmds.get(0).toString());
    }
  }

  //===================================================

  public void binaryGet(File file)
  {
    try
    {
      FileOutputStream fos = new FileOutputStream(file);
    }
    catch (IOException e){}
  }

  //===================================================

  public void asciiGet(File file)
  {
    try
    {
      FileWriter fw = new FileWriter(file);
    }
    catch (IOException e){}
  }

  //===================================================

}
